# Connect to Guardian API
library(guardianapi)
gu_api_key()
# Get data include key words "police" OR "law enforcement"
data_all <-gu_content(query='"police" OR "law enforcement"',
        from_date="2021-01-01",
        to_date="2023-03-31")
# Check the data
dim(data_all)
head(data_all)
# 24863 news articles collected
# Limit the data to only section_name == 'UK news'
data_uk_sec <- subset(data_all, section_name == 'UK news')
dim(data_uk_sec)
head(data_uk_sec) 
# Save as CSV file
library(dplyr)
my_data <- data_uk_sec %>% 
  select(-tags) # remove the non-informative column that contains nested lists
my_df <- as.data.frame(my_data)
write.csv(my_df, "raw_data.csv", row.names = TRUE)
# Read collected data
my_data <- read.csv('raw_data.csv')
dim(my_data)
[1] 3259   47
# Load required packages
library(tidyr)
library(quanteda)
library(caret)
library(dplyr)
library(tidytext)
library(syuzhet)
library(tidyverse)
library(quanteda.textstats)
library(ggplot2)
# Pre-processing
# Select required columns
my_data_hbd <- my_data %>% 
  select(headline, body_text, first_publication_date)
# Remove duplicates
my_data_hbd   <- distinct(my_data_hbd)
# Remove noise
my_data_hbd$body_text <- gsub("\\W+", " ", my_data_hbd$body_text)
# Tokenise the data and remove punctuation, numbers, and symbols
# Convert all text to lowercase and apply stemming
my_data_tokens <- tokens(my_data_hbd$body_text, remove_punct = TRUE, remove_numbers = TRUE, remove_symbols = TRUE) %>% tokens_remove(stopwords("english")) %>% tokens_tolower() %>% tokens_wordstem(language = quanteda_options("language_stemmer")) 
dim(my_data_hbd)# 3194 UK news articles after pre-processing
[1] 3194    3
head(my_data_hbd)
## Bing lexicon
# Try to use "bing" lexicon to get sentiment score for each news article
# Convert the data to a tidy format
tokens <- my_data_hbd %>%
  unnest_tokens(word, body_text) %>%
  anti_join(stop_words)
Joining with `by = join_by(word)`
# Load the lexicon
lexicon <- get_sentiments("bing")

# Join the lexicon with data
my_data_sentiment <- tokens %>%
  inner_join(lexicon)
Joining with `by = join_by(word)`
# Calculate the sentiment score
my_data_sentiment_scores <- my_data_sentiment %>%
  count(headline, sentiment) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>%
  mutate(sentiment_score = positive - negative)

# Visualise the sentiment scores
ggplot(my_data_sentiment_scores, aes(x = sentiment_score)) +
  geom_histogram(binwidth = 2, color = "black", fill = "lightblue") +
  labs(title = "Distribution of Sentiment Scores",
       x = "Sentiment Score",
       y = "Frequency") +
  theme_minimal()


# Categorise sentiment scores as positive, negative, or neutral
my_data_sentiment_category <- my_data_sentiment_scores %>%
  mutate(sentiment_category = ifelse(sentiment_score > 0, "Positive",
                                     ifelse(sentiment_score < 0, "Negative", "Neutral")))
  
# Count the number of articles in each sentiment category
my_data_sentiment_counts <- my_data_sentiment_category %>%
  count(sentiment_category)
print(my_data_sentiment_counts)
# Visualise the distribution of sentiment categories in articles
ggplot(data = my_data_sentiment_counts, aes(x = sentiment_category, y = n, fill = sentiment_category)) +
  geom_col() +
  geom_text(aes(label = n), position = position_stack(vjust = 0.5), color = "black", size = 4) +
  labs(x = "Sentiment Category", y = "Number of Articles", fill = "Sentiment Category") +
  ggtitle("Sentiment Categories Proportions Based on Bing Lexicon")

# Try to explore sentiment change over time based on Bing lexicon
# Calculate the sentiment score by date
sentiment_scores_date <- my_data_sentiment %>%
  count(first_publication_date, sentiment) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>%
  mutate(sentiment_score = positive - negative)

# Categorise sentiment scores as positive, negative, or neutral
sentiment_category_date <- sentiment_scores_date %>%
  mutate(sentiment_category = ifelse(sentiment_score > 0, "Positive",
                                     ifelse(sentiment_score < 0, "Negative", "Neutral")))

# Convert date to date object
sentiment_category_date$first_publication_date <- as.Date(sentiment_category_date$first_publication_date)

# Visualise the sentiment scores over time
ggplot(sentiment_category_date, aes(x = first_publication_date, y = sentiment_score)) +
  geom_line(color = "#000080") +
  geom_hline(yintercept = 0, color = "red") +
  labs(title = "Sentiment Score Over Time Based on Bing Lexicon",
       x = "Date",
       y = "Sentiment Score") +
  theme_minimal()

## Use get_sentiment() from syuzhet package to get sentiment score for each news article
library(syuzhet)
# Calculate the sentiment scores
my_data_hbd$sentiment_score <- get_sentiment(my_data_hbd$body_text)

# Categorise sentiment scores as positive, negative, or neutral
my_data_hbd$sentiment_category <- cut(my_data_hbd$sentiment_score, breaks=c(-Inf,-0.1,0.1,Inf), labels=c("Negative", "Neutral", "Positive"))

# Visualise the distribution of sentiment scores
ggplot(my_data_hbd, aes(x=sentiment_score, fill=sentiment_category)) +
  geom_histogram(binwidth=0.05, alpha=0.5, position="identity") +
  labs(title="Sentiment Scores Distribution", x="Sentiment Score", y="Count") +
  theme_minimal()

# Visualise the proportion of positive, negative, and neutral sentiment categories
ggplot(my_data_hbd, aes(x = sentiment_category, fill = sentiment_category)) +
  geom_bar() +
  geom_text(stat = "count", aes(label = after_stat(count)), 
            position = position_stack(vjust = 0.5)) +
  labs(title = "Sentiment Categories Proportions Based on Syuzhet Package",
       x = "Sentiment Category", y = "Number of Articles") +
  theme_minimal() +
  theme(panel.background = element_rect(fill = "grey90", color = NA),
        panel.grid.major = element_line(color = "white"),
        panel.grid.minor = element_blank())

# Try to explore sentiment change over time based on syuzhet package
# Convert first_publication_date to a date object
my_data_hbd$first_publication_date <- as.Date(my_data_hbd$first_publication_date)

# Aggregate sentiment score by date
sentiment_by_date <- my_data_hbd %>%
  group_by(first_publication_date) %>%
  summarise(sentiment_score = mean(sentiment_score))

# Create a line chart of sentiment score over time
ggplot(sentiment_by_date, aes(x=first_publication_date, y=sentiment_score)) +
  geom_line(color = "#000080") +
  geom_hline(yintercept = 0, color = "red") +
  labs(title="Sentiment Score over Time Based on Syuzhet Package", x="Date", y="Sentiment Score") +
  theme_minimal()

# Plot the sentiment score over time together (Bing Lexicon and Syuzhet Package)
library(patchwork)
library(scales)

# Create the first plot
plota <- ggplot(sentiment_category_date, aes(x = first_publication_date, y = sentiment_score)) +
  geom_line(color = "#000080") +
  geom_hline(yintercept = 0, color = "red") +
  labs(title = "Sentiment Score Over Time Based on Bing Lexicon",
       x = "Date",
       y = "Sentiment Score") +
  theme_minimal() +
  scale_x_date(date_breaks = "2 months", date_labels = "%b %y")

# Create the second plot
plotb <- ggplot(sentiment_by_date, aes(x=first_publication_date, y=sentiment_score)) +
  geom_line(color = "#000080") +
  geom_hline(yintercept = 0, color = "red") +
  labs(title="Sentiment Score over Time Based on Syuzhet Package", x="Date", y="Sentiment Score") +
  theme_minimal() +
  scale_x_date(date_breaks = "2 months", date_labels = "%b %y")

# Combine the plots vertically using patchwork
plota / plotb

## Supervised Machine Learning
# Try to apply machine learning technique, build a model to predict sentiment
# Map the values of the original column "sentiment_category" to integers
my_data_ml <- my_data_hbd %>%
  mutate(id = row_number(), # add a new column with row numbers
         sentiment_category = case_when(
           sentiment_category == "Positive" ~ 2,
           sentiment_category == "Negative" ~ 1,
           sentiment_category == "Neutral" ~ 0,
           TRUE ~ NA_real_ # to handle any other cases
         )) 
# Extract unigrams, bigrams as features
unigrams <- my_data_ml$body_text %>%
          tokens(remove_punct = T) %>%
          tokens_ngrams(1) %>%
          dfm()
bigrams <-  my_data_ml$body_text %>%
          tokens(remove_punct = T) %>%
          tokens_ngrams(2) %>%
          dfm()
# Apply sparsity correction to reduce the zero counts
unigrams_corrected <- dfm_trim(unigrams, sparsity = .90)
bigrams_corrected <- dfm_trim(bigrams, sparsity = .90)

# Check dimensions
dim(unigrams_corrected)
[1] 3194  622
# TFIDF-weighted ngrams
unigrams_tfidf <- dfm_tfidf(unigrams_corrected,
                           scheme_tf = 'count',
                           scheme_df = 'inverse')
bigrams_tfidf <- dfm_tfidf(bigrams_corrected,
                          scheme_tf = 'count',
                          scheme_df = 'inverse')

# Convert ngrams to data frames
news_unigrams <- convert(unigrams_tfidf, 'data.frame')
news_bigrams <- convert(bigrams_tfidf, 'data.frame')

# Add id column to ngrams to join unigrams and bigrams later
news_unigrams$id <- my_data_ml$id
news_bigrams$id <- my_data_ml$id

# Remove doc_id column
unigrams_tfidf_df <- subset(news_unigrams, select = -c(doc_id))
bigrams_tfidf_df <- subset(news_bigrams, select = -c(doc_id))

# Join the two datasets
ml_data <- bigrams_tfidf_df %>% left_join(unigrams_tfidf_df, by = "id")
# Get sentiment scores as features
ml_data$sentiment_score <- get_sentiment(my_data_ml$body_text)
# Add label column
ml_data$label <- my_data_ml$sentiment_category
# Remove the id column
ml_data <- ml_data %>% select(-id)
# Map labels
ml_data$label <- ifelse(ml_data$label == "2", "Positive", 
                   ifelse(ml_data$label == "1", "Negative", "Neutral")) 
# Convert label to factor
ml_data$label <- as.factor(ml_data$label) # convert label to factor
# Apply machine learning setting
library(caret)
set.seed(123)
for_training <- createDataPartition(y = ml_data$label,
                                  p = .8,
                                  list = FALSE)

training_data <- ml_data[for_training,]
testing_data <- ml_data[-for_training,]
# Set training control parameters
training_controls <- trainControl(method="cv", 
                                 number = 5,
                                 classProbs = T)
## Train the SVM model
model.svm <- train(label ~ .,
                  data = training_data,
                  method = "svmLinear",
                  trControl = training_controls,
                  preProcess = c('zv')
                 )
# Predict on the testing data
pred_svm <- predict(model.svm, testing_data)

# Evaluate the model
confusionMatrix(pred_svm, as.factor(testing_data$label), mode="prec_recall")
Confusion Matrix and Statistics

          Reference
Prediction Negative Neutral Positive
  Negative      431       5       42
  Neutral         0       0        0
  Positive       21       1      137

Overall Statistics
                                          
               Accuracy : 0.8917          
                 95% CI : (0.8649, 0.9147)
    No Information Rate : 0.7096          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.7274          
                                          
 Mcnemar's Test P-Value : 0.004637        

Statistics by Class:

                     Class: Negative Class: Neutral Class: Positive
Precision                     0.9017             NA          0.8616
Recall                        0.9535       0.000000          0.7654
F1                            0.9269             NA          0.8107
Prevalence                    0.7096       0.009419          0.2810
Detection Rate                0.6766       0.000000          0.2151
Detection Prevalence          0.7504       0.000000          0.2496
Balanced Accuracy             0.8497       0.500000          0.8587
## Train the Naive Bayes model
model.nb <- train(label ~ .,
                  data = training_data,
                  method = "naive_bayes",
                  trControl = training_controls,
                  preProcess = c('zv'))
# Predict on the testing data
pred_nb <- predict(model.nb, testing_data)

# Evaluate the model
confusionMatrix(pred_nb, as.factor(testing_data$label), mode="prec_recall")
Confusion Matrix and Statistics

          Reference
Prediction Negative Neutral Positive
  Negative      451       6      174
  Neutral         1       0        5
  Positive        0       0        0

Overall Statistics
                                         
               Accuracy : 0.708          
                 95% CI : (0.671, 0.7431)
    No Information Rate : 0.7096         
    P-Value [Acc > NIR] : 0.5544         
                                         
                  Kappa : 0.0169         
                                         
 Mcnemar's Test P-Value : <2e-16         

Statistics by Class:

                     Class: Negative Class: Neutral Class: Positive
Precision                     0.7147       0.000000              NA
Recall                        0.9978       0.000000           0.000
F1                            0.8329            NaN              NA
Prevalence                    0.7096       0.009419           0.281
Detection Rate                0.7080       0.000000           0.000
Detection Prevalence          0.9906       0.009419           0.000
Balanced Accuracy             0.5124       0.495246           0.500
# Set training control parameters
training_control <- trainControl(method = "cv", 
                                 number = 5, 
                                 verboseIter = FALSE)
# Train the Neural Network (NN) model
model.nn <- train(label ~ .,
                  data = training_data,
                  trControl = training_control,
                  method = "nnet",
                  MaxNWts = 10000,
                  maxit = 10)
# weights:  937
initial  value 3012.317783 
iter  10 value 215.740063
final  value 215.740063 
stopped after 10 iterations
# weights:  2805
initial  value 2890.927954 
iter  10 value 528.905183
final  value 528.905183 
stopped after 10 iterations
# weights:  4673
initial  value 1631.311019 
iter  10 value 81.493961
final  value 81.493961 
stopped after 10 iterations
# weights:  937
initial  value 2205.419836 
iter  10 value 375.374810
final  value 375.374810 
stopped after 10 iterations
# weights:  2805
initial  value 4080.726308 
iter  10 value 235.414024
final  value 235.414024 
stopped after 10 iterations
# weights:  4673
initial  value 2097.277411 
iter  10 value 441.662316
final  value 441.662316 
stopped after 10 iterations
# weights:  937
initial  value 1797.509188 
iter  10 value 454.281650
final  value 454.281650 
stopped after 10 iterations
# weights:  2805
initial  value 1522.231448 
iter  10 value 76.772175
final  value 76.772175 
stopped after 10 iterations
# weights:  4673
initial  value 2790.914079 
iter  10 value 149.951239
final  value 149.951239 
stopped after 10 iterations
# weights:  937
initial  value 2703.766546 
iter  10 value 231.052442
final  value 231.052442 
stopped after 10 iterations
# weights:  2805
initial  value 4350.443913 
iter  10 value 175.537187
final  value 175.537187 
stopped after 10 iterations
# weights:  4673
initial  value 1915.247997 
iter  10 value 67.946871
final  value 67.946871 
stopped after 10 iterations
# weights:  937
initial  value 2225.431471 
iter  10 value 371.623557
final  value 371.623557 
stopped after 10 iterations
# weights:  2805
initial  value 3346.243383 
iter  10 value 530.165846
final  value 530.165846 
stopped after 10 iterations
# weights:  4673
initial  value 2232.140065 
iter  10 value 860.015426
final  value 860.015426 
stopped after 10 iterations
# weights:  937
initial  value 2453.714672 
iter  10 value 459.896268
final  value 459.896268 
stopped after 10 iterations
# weights:  2805
initial  value 2948.106984 
iter  10 value 108.793768
final  value 108.793768 
stopped after 10 iterations
# weights:  4673
initial  value 3258.825327 
iter  10 value 93.066605
final  value 93.066605 
stopped after 10 iterations
# weights:  937
initial  value 1687.055584 
iter  10 value 175.437276
final  value 175.437276 
stopped after 10 iterations
# weights:  2805
initial  value 2275.372509 
iter  10 value 501.559418
final  value 501.559418 
stopped after 10 iterations
# weights:  4673
initial  value 2893.730150 
iter  10 value 83.585677
final  value 83.585677 
stopped after 10 iterations
# weights:  937
initial  value 2687.798446 
iter  10 value 280.086403
final  value 280.086403 
stopped after 10 iterations
# weights:  2805
initial  value 2445.555162 
iter  10 value 162.961999
final  value 162.961999 
stopped after 10 iterations
# weights:  4673
initial  value 2875.294472 
iter  10 value 172.413339
final  value 172.413339 
stopped after 10 iterations
# weights:  937
initial  value 2650.707482 
iter  10 value 549.678533
final  value 549.678533 
stopped after 10 iterations
# weights:  2805
initial  value 2829.549362 
iter  10 value 200.365957
final  value 200.365957 
stopped after 10 iterations
# weights:  4673
initial  value 2421.146339 
iter  10 value 79.565507
final  value 79.565507 
stopped after 10 iterations
# weights:  937
initial  value 2015.110133 
iter  10 value 184.705076
final  value 184.705076 
stopped after 10 iterations
# weights:  2805
initial  value 2169.741973 
iter  10 value 194.031525
final  value 194.031525 
stopped after 10 iterations
# weights:  4673
initial  value 2027.832382 
iter  10 value 84.826725
final  value 84.826725 
stopped after 10 iterations
# weights:  937
initial  value 2035.805987 
iter  10 value 361.348988
final  value 361.348988 
stopped after 10 iterations
# weights:  2805
initial  value 3230.270754 
iter  10 value 281.020927
final  value 281.020927 
stopped after 10 iterations
# weights:  4673
initial  value 1598.373160 
iter  10 value 258.396820
final  value 258.396820 
stopped after 10 iterations
# weights:  937
initial  value 1867.947297 
iter  10 value 235.219160
final  value 235.219160 
stopped after 10 iterations
# weights:  2805
initial  value 3175.434933 
iter  10 value 115.798166
final  value 115.798166 
stopped after 10 iterations
# weights:  4673
initial  value 3139.111259 
iter  10 value 125.734630
final  value 125.734630 
stopped after 10 iterations
# weights:  937
initial  value 1794.762865 
iter  10 value 402.371864
final  value 402.371864 
stopped after 10 iterations
# weights:  2805
initial  value 2308.690976 
iter  10 value 127.198993
final  value 127.198993 
stopped after 10 iterations
# weights:  4673
initial  value 3587.218789 
iter  10 value 297.734430
final  value 297.734430 
stopped after 10 iterations
# weights:  937
initial  value 2855.653728 
iter  10 value 439.341461
final  value 439.341461 
stopped after 10 iterations
# weights:  2805
initial  value 3714.714161 
iter  10 value 227.465379
final  value 227.465379 
stopped after 10 iterations
# weights:  4673
initial  value 2852.131228 
iter  10 value 134.047050
final  value 134.047050 
stopped after 10 iterations
# weights:  937
initial  value 2740.447362 
iter  10 value 882.667978
final  value 882.667978 
stopped after 10 iterations
# weights:  2805
initial  value 3239.106458 
iter  10 value 201.950394
final  value 201.950394 
stopped after 10 iterations
# weights:  4673
initial  value 2086.209728 
iter  10 value 61.603887
final  value 61.603887 
stopped after 10 iterations
# weights:  2805
initial  value 2785.648012 
iter  10 value 577.050423
final  value 577.050423 
stopped after 10 iterations
# Predict on the testing data
pred_nn <- predict(model.nn, testing_data)
# Evaluate the model
confusionMatrix(pred_nn, testing_data$label, mode = "prec_recall")
Confusion Matrix and Statistics

          Reference
Prediction Negative Neutral Positive
  Negative      447       1        2
  Neutral         0       0        0
  Positive        5       5      177

Overall Statistics
                                          
               Accuracy : 0.9796          
                 95% CI : (0.9654, 0.9891)
    No Information Rate : 0.7096          
    P-Value [Acc > NIR] : < 2e-16         
                                          
                  Kappa : 0.951           
                                          
 Mcnemar's Test P-Value : 0.06333         

Statistics by Class:

                     Class: Negative Class: Neutral Class: Positive
Precision                     0.9933             NA          0.9465
Recall                        0.9889       0.000000          0.9888
F1                            0.9911             NA          0.9672
Prevalence                    0.7096       0.009419          0.2810
Detection Rate                0.7017       0.000000          0.2779
Detection Prevalence          0.7064       0.000000          0.2936
Balanced Accuracy             0.9864       0.500000          0.9835
## Unsupervised Machine Learning
# Try to apply k-means clustering model using the extracted features
# Use ngrams frequencies as features
ml_grams <- bigrams_tfidf_df %>% left_join(unigrams_tfidf_df, by = "id")
# Remove the id column
ml_grams <- ml_grams %>% select(-id)
# Use the k-means model on n-grams
# Determine k
wss <- numeric()
for(i in 1:10){
  ngrams_kmeans_temp <- kmeans(x = ml_grams,
                             centers = i,
                             iter.max = 20,
                             nstart = 10)
  wss[i] <- ngrams_kmeans_temp$tot.withinss
}
{plot(wss, type='b')} 

# Settle for k=2
ngrams_kmeans <- kmeans(ml_grams,
                      centers = 2,
                      iter.max = 10,
                      nstart = 10)
# Look at the clusters by their most prominent ngram differences
# Cluster 1
cat("Cluster 1: \n")
Cluster 1: 
print(head(sort(ngrams_kmeans$centers[1, ], decreasing = TRUE), n = 20))
         royal            her          state           will        will_be 
      53.78888       31.04378       29.11604       23.43286       22.49379 
         prime prime_minister       minister            she           here 
      22.42150       19.98957       19.88383       18.28677       15.88477 
       service          world     members_of          wales            new 
      15.34549       14.74088       13.74756       13.56348       13.49116 
         death              i        country          today        members 
      13.31633       13.06975       12.94854       12.84766       12.54317 
# Cluster 2
cat("\nCluster 2: \n")

Cluster 2: 
print(head(sort(ngrams_kmeans$centers[2, ], decreasing = TRUE), n = 20))
       her        she          i        his        met         he    the_met 
 0.8976625  0.8137124  0.6721067  0.5938533  0.5798295  0.5744155  0.5516751 
     women        you   officers        him      court     people         we 
 0.5282233  0.4786335  0.4515644  0.4432352  0.4309058  0.4273825  0.4235406 
    family          t government     he_was         uk     london 
 0.4209670  0.4143618  0.4004978  0.3901206  0.3864412  0.3794096 
# Use the k-means model on sentiment score
# Determine k
wss <- numeric()
for(i in 1:10){
  sentiment_kmeans_temp <- kmeans(x = ml_data$sentiment_score,
                             centers = i,
                             iter.max = 10,
                             nstart = 10)
  wss[i] <- sentiment_kmeans_temp$tot.withinss
}
{plot(wss, type='b')}

# Settle for k=3
sentiment_kmeans <- kmeans(ml_data$sentiment_score,
                      centers = 3,
                      iter.max = 5,
                      nstart = 5)
# Print the centers of each sentiment cluster
print(sentiment_kmeans$centers[1, ])
      1 
1.58212 
print(sentiment_kmeans$centers[2, ])
        2 
-10.96622 
print(sentiment_kmeans$centers[3, ])
       3 
99.87857 
# Visualise the clusters
# Add the cluster labels to the original data frame
ml_data$cluster <- factor(sentiment_kmeans$cluster)

# Create the boxplot
ggplot(ml_data, aes(x = cluster, y = sentiment_score)) +
  geom_boxplot() +
  xlab("Cluster") +
  ylab("Sentiment Score")

### Topic Modelling
# Load required packages
library(tidyr)
library(quanteda)
library(caret)
library(dplyr)
library(tidytext)
library(topicmodels)
library(ldatuning)
library(stm)
library(wordcloud)
library(tm)
library(ggplot2)
# Prepare the data and select required columns
my_data_hbd <- my_data %>% 
  select(headline, body_text, first_publication_date)
# Remove duplicates
my_data_hbd <- distinct(my_data_hbd)
# Remove any noise in the data
my_data_hbd$body_text <- gsub("\\W+", " ", my_data_hbd$body_text)
# Tokenise the data and remove punctuation, numbers, and symbols
# Convert all text to lowercase, apply stemming, and remove non-informative words for topic modelling purposes
tm_tokens <- tokens(my_data_hbd$body_text, remove_punct = TRUE, remove_numbers = TRUE, remove_symbols = TRUE) %>% tokens_remove(stopwords("english")) %>% tokens_tolower() %>% tokens_wordstem(language = quanteda_options("language_stemmer")) %>% tokens_remove(c("polic", "offic", "peopl", "year", "day", "will", "also", "t", "s", "can", "uk", "british"))
# Create a Document Feature Matrix (dfm)
dfm_tm <- dfm(tm_tokens)

# Trim the dfm for frequently and rarely occurring terms
dfm.trim <- dfm_trim(dfm_tm, min_docfreq = 0.075, max_docfreq = 0.9, docfreq_type = "prop")
dfm.trim
Document-feature matrix of: 3,194 documents, 764 features (83.44% sparse) and 0 docvars.
       features
docs    immedi govern quit human right issu stop small cross across
  text1      2      1    1     6     8    1    1     1     2      1
  text2      0      3    0     0     0    1    1     0     0      2
  text3      0      4    1     2     1    0    1     0     0      0
  text4      0      2    0     0     0    1    0     3     1      2
  text5      0      1    0     0     1    2    0     0     0      0
  text6      0      0    0     0     0    0   11     0     0      2
[ reached max_ndoc ... 3,188 more documents, reached max_nfeat ... 754 more features ]
## LDA topic modelling
n.topics <- 5
dfm2topicmodels <- convert(dfm.trim, to = "topicmodels")
lda.model <- LDA(dfm2topicmodels, n.topics, control = list(seed=111))
lda.model
A LDA_VEM topic model with 5 topics.
# Extract the topic-term matrix (beta) from the LDA model
beta_topics <- tidy(lda.model, matrix = "beta")
beta_topics
# Group the terms by topics
beta_top_terms <- beta_topics %>%
  group_by(topic) %>%
  slice_max(beta, n = 10) %>%
  ungroup() %>%
  arrange(topic, -beta)
# Display the group terms 
beta_top_terms %>%
  mutate(term = reorder_within(term, beta, topic)) %>%
  ggplot(aes(beta, term, fill = factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  scale_y_reordered()

# Top 10 terms for each topics 
as.data.frame(terms(lda.model, 10))

# List the topic number for each news article in the corpus.
data.frame(Topic = topics(lda.model))
# Convert the topic model to a data frame
topics_df <- data.frame(topics(lda.model))

# Plot the count of topics
ggplot(topics_df, aes(x = factor(topics(lda.model)))) +
  geom_bar(aes(fill = factor(topics(lda.model))), alpha = 0.8) +
  scale_fill_discrete(name = "Topic") +
  labs(title = "Topic Distribution Based on Latent Dirichlet Allocation (LDA)", x = "Topic", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5),
        axis.text.x = element_text(angle = 45, hjust = 1),
        axis.title = element_text(size = 12),
        panel.background = element_rect(fill = "white"),
        panel.grid.major = element_line(colour = "gray90")) +
  geom_text(stat='count', aes(label=..count..), vjust=-0.5)

## Structural Topic Modeling (STM)
n.topics <- 5
dfm2stm <- convert(dfm.trim, to = "stm")
modell.stm <- stm(dfm2stm$documents, dfm2stm$vocab, K = n.topics, data = dfm2stm$meta, init.type = "Spectral")
Beginning Spectral Initialization 
     Calculating the gram matrix...
     Finding anchor words...
    .....
     Recovering initialization...
    .......
Initialization complete.
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 1 (approx. per word bound = -6.371) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 2 (approx. per word bound = -6.316, relative change = 8.618e-03) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 3 (approx. per word bound = -6.296, relative change = 3.166e-03) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 4 (approx. per word bound = -6.288, relative change = 1.287e-03) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 5 (approx. per word bound = -6.284, relative change = 6.390e-04) 
Topic 1: court, investig, case, alleg, told 
 Topic 2: incid, fire, investig, area, servic 
 Topic 3: govern, met, home, protest, forc 
 Topic 4: famili, old, murder, told, attack 
 Topic 5: royal, time, minist, state, famili 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 6 (approx. per word bound = -6.282, relative change = 3.647e-04) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 7 (approx. per word bound = -6.280, relative change = 2.298e-04) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 8 (approx. per word bound = -6.279, relative change = 1.553e-04) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 9 (approx. per word bound = -6.279, relative change = 1.102e-04) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 10 (approx. per word bound = -6.278, relative change = 8.075e-05) 
Topic 1: court, investig, case, alleg, told 
 Topic 2: incid, investig, fire, area, man 
 Topic 3: met, govern, home, forc, protest 
 Topic 4: famili, old, told, murder, friend 
 Topic 5: royal, state, time, minist, servic 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 11 (approx. per word bound = -6.278, relative change = 6.053e-05) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 12 (approx. per word bound = -6.278, relative change = 4.604e-05) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 13 (approx. per word bound = -6.277, relative change = 3.556e-05) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 14 (approx. per word bound = -6.277, relative change = 2.763e-05) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 15 (approx. per word bound = -6.277, relative change = 2.159e-05) 
Topic 1: court, investig, case, alleg, charg 
 Topic 2: incid, investig, fire, area, arrest 
 Topic 3: met, govern, home, forc, protest 
 Topic 4: famili, told, old, murder, say 
 Topic 5: royal, state, minist, time, servic 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 16 (approx. per word bound = -6.277, relative change = 1.699e-05) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 17 (approx. per word bound = -6.277, relative change = 1.340e-05) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Completing Iteration 18 (approx. per word bound = -6.277, relative change = 1.054e-05) 
.......................................................................................................
Completed E-Step (1 seconds). 
Completed M-Step. 
Model Converged 
# Top 10 terms for each topic
as.data.frame(t(labelTopics(modell.stm, n = 10)$prob))

# Generate a word cloud for the first topic
par(mar=c(0.5, 0.5, 0.5, 0.5))
cloud(modell.stm, topic = 1, scale = c(2.25,.5))


# Plot a summary of the estimated topic shares for the corpus
plot(modell.stm, type = "summary", text.cex = 0.5, main = "Topic shares on the corpus as a whole", xlab = "estimated share of topics")

# Print the top 5 terms for each of the 5 topics
labelTopics(modell.stm,topics = c(1:5), n=10)
Topic 1 Top Words:
     Highest Prob: court, investig, case, alleg, charg, evid, told, inquiri, victim, sexual 
     FREX: alleg, court, sexual, prosecut, charg, evid, rape, case, sentenc, offenc 
     Lift: rape, prosecutor, prosecut, sexual, judg, alleg, convict, jail, sentenc, court 
     Score: rape, court, sexual, investig, alleg, prosecut, guilti, juri, sentenc, convict 
Topic 2 Top Words:
     Highest Prob: incid, investig, arrest, area, man, fire, london, servic, report, two 
     FREX: incid, fire, area, road, scene, arrest, hospit, manchest, west, search 
     Lift: fire, condit, vehicl, incid, road, suspicion, locat, scene, area, injur 
     Score: fire, incid, investig, road, area, hospit, man, search, scene, resid 
Topic 3 Top Words:
     Highest Prob: met, govern, home, forc, protest, women, public, report, say, crime 
     FREX: protest, commission, govern, secretari, organis, chief, violenc, labour, right, crime 
     Lift: protest, patel, commission, anti, mayor, tackl, institut, improv, polici, cultur 
     Score: protest, govern, commission, secretari, crime, patel, everard, met, minist, women 
Topic 4 Top Words:
     Highest Prob: famili, told, old, say, murder, one, go, friend, time, just 
     FREX: friend, mother, boy, kill, love, know, son, school, dog, girl 
     Lift: dog, daughter, teenag, parent, didn, friend, sister, boy, son, father 
     Score: dog, juri, mother, murder, famili, boy, stab, friend, son, love 
Topic 5 Top Words:
     Highest Prob: royal, state, minist, time, servic, famili, new, one, first, countri 
     FREX: royal, state, ireland, northern, minist, prime, countri, pay, respect, world 
     Lift: ireland, royal, northern, prime, pay, respect, state, world, visit, great 
     Score: ireland, royal, minist, northern, prime, tribut, state, leader, parliament, countri 
# Plot histograms of the estimated topic shares within the documents for 3 randomly selected topics
plot(modell.stm, type = "hist", topics = sample(1:n.topics, size = 3), main = "histogram of the topic shares within the documents")


# Plot the top terms for each of the 5 topics
plot(modell.stm, type = "labels", topics = c(1,2,3,4,5), main = "Topic terms")

library(ggplot2)
library(dplyr)
# Identify the most probable topic for each document
top_topics <- apply(modell.stm$theta, 1, which.max)
# Add the topic labels to the original data frame
my_data_hbd$Topic_stm <- top_topics
# Calculate the count of each topic
df_topic_counts <- my_data_hbd %>%
  group_by(Topic_stm) %>%
  summarize(count = n())

# Create the plot
ggplot(df_topic_counts, aes(x = factor(Topic_stm), y = count, fill = factor(Topic_stm))) +
  geom_bar(stat = "identity", alpha = 0.8) +
  scale_fill_discrete(name = "Topic") +
  labs(title = "Topic Distribution Based on Structural Topic Modeling (STM)", x = "Topic", y = "Count") +
  theme(plot.title = element_text(hjust = 0.5),
        axis.text.x = element_text(angle = 45, hjust = 1),
        axis.title = element_text(size = 12),
        panel.background = element_rect(fill = "white"),
        panel.grid.major = element_line(colour = "gray90")) +
  geom_text(aes(label = count), vjust=-0.5)

## To see how the topics changed over time (LDA model)

# Add the topic labels to the original data frame
my_data_hbd$Topic_lda <- topics(lda.model)

# Convert the date column to a Date object
my_data_hbd$first_publication_date <- as.Date(my_data_hbd$first_publication_date, format = "%Y-%m-%d")

# Calculate the proportion of each topic by year
topic_proportions <- my_data_hbd %>%
  group_by(Topic_lda, year = lubridate::year(first_publication_date)) %>%
  summarise(count = n(), .groups = 'drop') %>%
  ungroup() %>%
  group_by(year) %>%
  mutate(proportion = count / sum(count))

# Plot the topic proportions over time
ggplot(topic_proportions, aes(x = year, y = proportion, color = factor(Topic_lda))) +
  geom_line() +
  labs(x = "Year", y = "Proportion", color = "Topic") +
  theme_bw()


# Calculate the proportion of each topic by half-year
topic_proportions_half_year <- my_data_hbd %>%
  group_by(Topic_lda, year = lubridate::year(first_publication_date), half_year = lubridate::floor_date(first_publication_date, unit = "6 months")) %>%
  summarise(count = n(), .groups = 'drop') %>%
  ungroup() %>%
  group_by(year, half_year) %>%
  mutate(proportion = count / sum(count))

# Plot the topic proportions over time
ggplot(topic_proportions_half_year, aes(x = half_year, y = proportion, color = factor(Topic_lda))) +
  geom_line() +
  labs(x = "Half-Year", y = "Proportion", color = "Topic") +
  theme_bw()

## To see how the topics changed over time (STM model)

# Identify the most probable topic for each document
top_topics <- apply(modell.stm$theta, 1, which.max)
# Add the topic labels to the original data frame
my_data_hbd$Topic_stm <- top_topics

# Calculate the proportion of each topic by year
topic_proportions_stm <- my_data_hbd %>%
  group_by(Topic_stm, year = lubridate::year(first_publication_date)) %>%
  summarise(count = n(), .groups = 'drop') %>%
  ungroup() %>%
  group_by(year) %>%
  mutate(proportion = count / sum(count))

# Plot the topic proportions over time
ggplot(topic_proportions_stm, aes(x = year, y = proportion, color = factor(Topic_stm))) +
  geom_line() +
  labs(x = "Year", y = "Proportion", color = "Topic") +
  theme_bw()


# Calculate the proportion of each topic by half-year
topic_proportions_stm_half_year <- my_data_hbd %>%
  group_by(Topic_stm, year = lubridate::year(first_publication_date), half_year = lubridate::floor_date(first_publication_date, unit = "6 months")) %>%
  summarise(count = n(), .groups = 'drop') %>%
  ungroup() %>%
  group_by(year, half_year) %>%
  mutate(proportion = count / sum(count))


# Plot the topic proportions over time
ggplot(topic_proportions_stm_half_year, aes(x = half_year, y = proportion, color = factor(Topic_stm))) +
  geom_line() +
  labs(x = "Half-Year", y = "Proportion", color = "Topic") +
  theme_bw()

# Refine the graphs, add the top 5 terms for each topic to the legend (LDA model)

# Extract the top 5 terms for each topic
top_terms <- terms(lda.model, 5)

# Create label for color legend
legend_label <- paste("Top 5 Terms\n",
                      "1: ", paste(top_terms[,1], collapse = ", "), "\n",
                      "2: ", paste(top_terms[,2], collapse = ", "), "\n",
                      "3: ", paste(top_terms[,3], collapse = ", "), "\n",
                      "4: ", paste(top_terms[,4], collapse = ", "), "\n",
                      "5: ", paste(top_terms[,5], collapse = ", "), sep = "")

# Plot the topic proportions over time
ggplot(topic_proportions_half_year, aes(x = half_year, y = proportion, color = factor(Topic_lda))) +
  geom_line() +
  labs(x = "Half-Year", y = "Proportion", 
       color = legend_label,
       subtitle = "Topic Changes Over Time Using LDA Topic Modelling") +
  theme_bw()

# Refine the graphs, add the top 5 terms for each topic to the legend (STM model)

# Extract the top 5 terms for each topic
top_terms_stm <- labelTopics(modell.stm, topics = c(1:5), n=5)
top_terms_stm$frex
     [,1]      [,2]         [,3]      [,4]        [,5]     
[1,] "alleg"   "court"      "sexual"  "prosecut"  "charg"  
[2,] "incid"   "fire"       "area"    "road"      "scene"  
[3,] "protest" "commission" "govern"  "secretari" "organis"
[4,] "friend"  "mother"     "boy"     "kill"      "love"   
[5,] "royal"   "state"      "ireland" "northern"  "minist" 
# Create label for color legend
legend_label_stm <- paste("Top 5 Terms\n",
                      "1: ", paste(top_terms_stm$frex[1,], collapse = ", "), "\n",
                      "2: ", paste(top_terms_stm$frex[2,], collapse = ", "), "\n",
                      "3: ", paste(top_terms_stm$frex[3,], collapse = ", "), "\n",
                      "4: ", paste(top_terms_stm$frex[4,], collapse = ", "), "\n",
                      "5: ", paste(top_terms_stm$frex[5,], collapse = ", "), sep = "")

# Plot the topic proportions over time
ggplot(topic_proportions_stm_half_year, aes(x = half_year, y = proportion, color = factor(Topic_stm))) +
  geom_line() +
  labs(x = "Half-Year", y = "Proportion", 
       color = legend_label_stm,
       subtitle = "Topic Changes Over Time Using Structural Topic Modeling (STM)") +
  theme_bw()

# Try to combine sentiment and topic changes over time
library(patchwork)

# Create a line chart of sentiment score over time
p1 <- ggplot(sentiment_by_date, aes(x = first_publication_date, y = sentiment_score)) +
  geom_line() +
  geom_hline(yintercept = 0, linetype = "dashed", color = "blue") +
  labs(title = "Sentiment Score over Time Based on Syuzhet Package", x = "Date", y = "Sentiment Score") +
  theme_minimal()+
  theme(plot.title = element_text(size = 11))

# Plot the topic proportions over time using LDA
p2 <- ggplot(topic_proportions_half_year, aes(x = half_year, y = proportion, color = factor(Topic_lda))) +
  geom_line() +
  labs(x = "Half-Year", y = "Proportion", 
       color = "Topic", subtitle = "Topic Changes Over Time Using LDA Topic Modelling") +
  theme_bw()

# Plot the topic proportions over time using STM
p3 <- ggplot(topic_proportions_stm_half_year, aes(x = half_year, y = proportion, color = factor(Topic_stm))) +
  geom_line() +
  labs(x = "Half-Year", y = "Proportion", 
       color = "Topic",
       subtitle = "Topic Changes Over Time Using Structural Topic Modeling") +
  theme_bw()

# Combine the three plots vertically
p1 + p2 + p3 + plot_layout(ncol = 1)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpgYGB7cn0KIyBDb25uZWN0IHRvIEd1YXJkaWFuIEFQSQpsaWJyYXJ5KGd1YXJkaWFuYXBpKQpndV9hcGlfa2V5KCkKYGBgCgpgYGB7cn0KIyBHZXQgZGF0YSBpbmNsdWRlIGtleSB3b3JkcyAicG9saWNlIiBPUiAibGF3IGVuZm9yY2VtZW50IgpkYXRhX2FsbCA8LWd1X2NvbnRlbnQocXVlcnk9JyJwb2xpY2UiIE9SICJsYXcgZW5mb3JjZW1lbnQiJywKICAgICAgICBmcm9tX2RhdGU9IjIwMjEtMDEtMDEiLAogICAgICAgIHRvX2RhdGU9IjIwMjMtMDMtMzEiKQpgYGAKYGBge3J9CiMgQ2hlY2sgdGhlIGRhdGEKZGltKGRhdGFfYWxsKQpoZWFkKGRhdGFfYWxsKQojIDI0ODYzIG5ld3MgYXJ0aWNsZXMgY29sbGVjdGVkCmBgYAoKYGBge3J9CiMgTGltaXQgdGhlIGRhdGEgdG8gb25seSBzZWN0aW9uX25hbWUgPT0gJ1VLIG5ld3MnCmRhdGFfdWtfc2VjIDwtIHN1YnNldChkYXRhX2FsbCwgc2VjdGlvbl9uYW1lID09ICdVSyBuZXdzJykKZGltKGRhdGFfdWtfc2VjKQpoZWFkKGRhdGFfdWtfc2VjKSAKYGBgCgoKYGBge3J9CiMgU2F2ZSBhcyBDU1YgZmlsZQpsaWJyYXJ5KGRwbHlyKQpteV9kYXRhIDwtIGRhdGFfdWtfc2VjICU+JSAKICBzZWxlY3QoLXRhZ3MpICMgcmVtb3ZlIHRoZSBub24taW5mb3JtYXRpdmUgY29sdW1uIHRoYXQgY29udGFpbnMgbmVzdGVkIGxpc3RzCm15X2RmIDwtIGFzLmRhdGEuZnJhbWUobXlfZGF0YSkKd3JpdGUuY3N2KG15X2RmLCAicmF3X2RhdGEuY3N2Iiwgcm93Lm5hbWVzID0gVFJVRSkKYGBgCgpgYGB7cn0KIyBSZWFkIGNvbGxlY3RlZCBkYXRhCm15X2RhdGEgPC0gcmVhZC5jc3YoJ3Jhd19kYXRhLmNzdicpCmRpbShteV9kYXRhKSMgMzI1OSBVSyBuZXdzIGFydGljbGVzIApgYGAKCmBgYHtyfQojIExvYWQgcmVxdWlyZWQgcGFja2FnZXMKbGlicmFyeSh0aWR5cikKbGlicmFyeShxdWFudGVkYSkKbGlicmFyeShjYXJldCkKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeShzeXV6aGV0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShxdWFudGVkYS50ZXh0c3RhdHMpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgpgYGB7cn0KIyBQcmUtcHJvY2Vzc2luZwojIFNlbGVjdCByZXF1aXJlZCBjb2x1bW5zCm15X2RhdGFfaGJkIDwtIG15X2RhdGEgJT4lIAogIHNlbGVjdChoZWFkbGluZSwgYm9keV90ZXh0LCBmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKQojIFJlbW92ZSBkdXBsaWNhdGVzCm15X2RhdGFfaGJkICAgPC0gZGlzdGluY3QobXlfZGF0YV9oYmQpCiMgUmVtb3ZlIG5vaXNlCm15X2RhdGFfaGJkJGJvZHlfdGV4dCA8LSBnc3ViKCJcXFcrIiwgIiAiLCBteV9kYXRhX2hiZCRib2R5X3RleHQpCiMgVG9rZW5pc2UgdGhlIGRhdGEgYW5kIHJlbW92ZSBwdW5jdHVhdGlvbiwgbnVtYmVycywgYW5kIHN5bWJvbHMKIyBDb252ZXJ0IGFsbCB0ZXh0IHRvIGxvd2VyY2FzZSBhbmQgYXBwbHkgc3RlbW1pbmcKbXlfZGF0YV90b2tlbnMgPC0gdG9rZW5zKG15X2RhdGFfaGJkJGJvZHlfdGV4dCwgcmVtb3ZlX3B1bmN0ID0gVFJVRSwgcmVtb3ZlX251bWJlcnMgPSBUUlVFLCByZW1vdmVfc3ltYm9scyA9IFRSVUUpICU+JSB0b2tlbnNfcmVtb3ZlKHN0b3B3b3JkcygiZW5nbGlzaCIpKSAlPiUgdG9rZW5zX3RvbG93ZXIoKSAlPiUgdG9rZW5zX3dvcmRzdGVtKGxhbmd1YWdlID0gcXVhbnRlZGFfb3B0aW9ucygibGFuZ3VhZ2Vfc3RlbW1lciIpKSAKYGBgCgpgYGB7cn0KZGltKG15X2RhdGFfaGJkKSMgMzE5NCBVSyBuZXdzIGFydGljbGVzIGFmdGVyIHByZS1wcm9jZXNzaW5nCmhlYWQobXlfZGF0YV9oYmQpCmBgYAoKCmBgYHtyfQojIyBCaW5nIGxleGljb24KIyBUcnkgdG8gdXNlICJiaW5nIiBsZXhpY29uIHRvIGdldCBzZW50aW1lbnQgc2NvcmUgZm9yIGVhY2ggbmV3cyBhcnRpY2xlCiMgQ29udmVydCB0aGUgZGF0YSB0byBhIHRpZHkgZm9ybWF0CnRva2VucyA8LSBteV9kYXRhX2hiZCAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIGJvZHlfdGV4dCkgJT4lCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpCiMgTG9hZCB0aGUgbGV4aWNvbgpsZXhpY29uIDwtIGdldF9zZW50aW1lbnRzKCJiaW5nIikKCiMgSm9pbiB0aGUgbGV4aWNvbiB3aXRoIGRhdGEKbXlfZGF0YV9zZW50aW1lbnQgPC0gdG9rZW5zICU+JQogIGlubmVyX2pvaW4obGV4aWNvbikKCiMgQ2FsY3VsYXRlIHRoZSBzZW50aW1lbnQgc2NvcmUKbXlfZGF0YV9zZW50aW1lbnRfc2NvcmVzIDwtIG15X2RhdGFfc2VudGltZW50ICU+JQogIGNvdW50KGhlYWRsaW5lLCBzZW50aW1lbnQpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzZW50aW1lbnQsIHZhbHVlc19mcm9tID0gbiwgdmFsdWVzX2ZpbGwgPSAwKSAlPiUKICBtdXRhdGUoc2VudGltZW50X3Njb3JlID0gcG9zaXRpdmUgLSBuZWdhdGl2ZSkKCiMgVmlzdWFsaXNlIHRoZSBzZW50aW1lbnQgc2NvcmVzCmdncGxvdChteV9kYXRhX3NlbnRpbWVudF9zY29yZXMsIGFlcyh4ID0gc2VudGltZW50X3Njb3JlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMiwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBTZW50aW1lbnQgU2NvcmVzIiwKICAgICAgIHggPSAiU2VudGltZW50IFNjb3JlIiwKICAgICAgIHkgPSAiRnJlcXVlbmN5IikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBDYXRlZ29yaXNlIHNlbnRpbWVudCBzY29yZXMgYXMgcG9zaXRpdmUsIG5lZ2F0aXZlLCBvciBuZXV0cmFsCm15X2RhdGFfc2VudGltZW50X2NhdGVnb3J5IDwtIG15X2RhdGFfc2VudGltZW50X3Njb3JlcyAlPiUKICBtdXRhdGUoc2VudGltZW50X2NhdGVnb3J5ID0gaWZlbHNlKHNlbnRpbWVudF9zY29yZSA+IDAsICJQb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc2VudGltZW50X3Njb3JlIDwgMCwgIk5lZ2F0aXZlIiwgIk5ldXRyYWwiKSkpCiAgCiMgQ291bnQgdGhlIG51bWJlciBvZiBhcnRpY2xlcyBpbiBlYWNoIHNlbnRpbWVudCBjYXRlZ29yeQpteV9kYXRhX3NlbnRpbWVudF9jb3VudHMgPC0gbXlfZGF0YV9zZW50aW1lbnRfY2F0ZWdvcnkgJT4lCiAgY291bnQoc2VudGltZW50X2NhdGVnb3J5KQpwcmludChteV9kYXRhX3NlbnRpbWVudF9jb3VudHMpCmBgYAoKYGBge3J9CiMgVmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2Ygc2VudGltZW50IGNhdGVnb3JpZXMgaW4gYXJ0aWNsZXMKZ2dwbG90KGRhdGEgPSBteV9kYXRhX3NlbnRpbWVudF9jb3VudHMsIGFlcyh4ID0gc2VudGltZW50X2NhdGVnb3J5LCB5ID0gbiwgZmlsbCA9IHNlbnRpbWVudF9jYXRlZ29yeSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArCiAgbGFicyh4ID0gIlNlbnRpbWVudCBDYXRlZ29yeSIsIHkgPSAiTnVtYmVyIG9mIEFydGljbGVzIiwgZmlsbCA9ICJTZW50aW1lbnQgQ2F0ZWdvcnkiKSArCiAgZ2d0aXRsZSgiU2VudGltZW50IENhdGVnb3JpZXMgUHJvcG9ydGlvbnMgQmFzZWQgb24gQmluZyBMZXhpY29uIikKCmBgYAoKYGBge3J9CiMgVHJ5IHRvIGV4cGxvcmUgc2VudGltZW50IGNoYW5nZSBvdmVyIHRpbWUgYmFzZWQgb24gQmluZyBsZXhpY29uCiMgQ2FsY3VsYXRlIHRoZSBzZW50aW1lbnQgc2NvcmUgYnkgZGF0ZQpzZW50aW1lbnRfc2NvcmVzX2RhdGUgPC0gbXlfZGF0YV9zZW50aW1lbnQgJT4lCiAgY291bnQoZmlyc3RfcHVibGljYXRpb25fZGF0ZSwgc2VudGltZW50KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc2VudGltZW50LCB2YWx1ZXNfZnJvbSA9IG4sIHZhbHVlc19maWxsID0gMCkgJT4lCiAgbXV0YXRlKHNlbnRpbWVudF9zY29yZSA9IHBvc2l0aXZlIC0gbmVnYXRpdmUpCgojIENhdGVnb3Jpc2Ugc2VudGltZW50IHNjb3JlcyBhcyBwb3NpdGl2ZSwgbmVnYXRpdmUsIG9yIG5ldXRyYWwKc2VudGltZW50X2NhdGVnb3J5X2RhdGUgPC0gc2VudGltZW50X3Njb3Jlc19kYXRlICU+JQogIG11dGF0ZShzZW50aW1lbnRfY2F0ZWdvcnkgPSBpZmVsc2Uoc2VudGltZW50X3Njb3JlID4gMCwgIlBvc2l0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzZW50aW1lbnRfc2NvcmUgPCAwLCAiTmVnYXRpdmUiLCAiTmV1dHJhbCIpKSkKCiMgQ29udmVydCBkYXRlIHRvIGRhdGUgb2JqZWN0CnNlbnRpbWVudF9jYXRlZ29yeV9kYXRlJGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUgPC0gYXMuRGF0ZShzZW50aW1lbnRfY2F0ZWdvcnlfZGF0ZSRmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKQoKIyBWaXN1YWxpc2UgdGhlIHNlbnRpbWVudCBzY29yZXMgb3ZlciB0aW1lCmdncGxvdChzZW50aW1lbnRfY2F0ZWdvcnlfZGF0ZSwgYWVzKHggPSBmaXJzdF9wdWJsaWNhdGlvbl9kYXRlLCB5ID0gc2VudGltZW50X3Njb3JlKSkgKwogIGdlb21fbGluZShjb2xvciA9ICIjMDAwMDgwIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIlNlbnRpbWVudCBTY29yZSBPdmVyIFRpbWUgQmFzZWQgb24gQmluZyBMZXhpY29uIiwKICAgICAgIHggPSAiRGF0ZSIsCiAgICAgICB5ID0gIlNlbnRpbWVudCBTY29yZSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKYGBge3J9CiMjIFVzZSBnZXRfc2VudGltZW50KCkgZnJvbSBzeXV6aGV0IHBhY2thZ2UgdG8gZ2V0IHNlbnRpbWVudCBzY29yZSBmb3IgZWFjaCBuZXdzIGFydGljbGUKbGlicmFyeShzeXV6aGV0KQojIENhbGN1bGF0ZSB0aGUgc2VudGltZW50IHNjb3JlcwpteV9kYXRhX2hiZCRzZW50aW1lbnRfc2NvcmUgPC0gZ2V0X3NlbnRpbWVudChteV9kYXRhX2hiZCRib2R5X3RleHQpCgojIENhdGVnb3Jpc2Ugc2VudGltZW50IHNjb3JlcyBhcyBwb3NpdGl2ZSwgbmVnYXRpdmUsIG9yIG5ldXRyYWwKbXlfZGF0YV9oYmQkc2VudGltZW50X2NhdGVnb3J5IDwtIGN1dChteV9kYXRhX2hiZCRzZW50aW1lbnRfc2NvcmUsIGJyZWFrcz1jKC1JbmYsLTAuMSwwLjEsSW5mKSwgbGFiZWxzPWMoIk5lZ2F0aXZlIiwgIk5ldXRyYWwiLCAiUG9zaXRpdmUiKSkKCiMgVmlzdWFsaXNlIHRoZSBkaXN0cmlidXRpb24gb2Ygc2VudGltZW50IHNjb3JlcwpnZ3Bsb3QobXlfZGF0YV9oYmQsIGFlcyh4PXNlbnRpbWVudF9zY29yZSwgZmlsbD1zZW50aW1lbnRfY2F0ZWdvcnkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9MC4wNSwgYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZT0iU2VudGltZW50IFNjb3JlcyBEaXN0cmlidXRpb24iLCB4PSJTZW50aW1lbnQgU2NvcmUiLCB5PSJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KIyBWaXN1YWxpc2UgdGhlIHByb3BvcnRpb24gb2YgcG9zaXRpdmUsIG5lZ2F0aXZlLCBhbmQgbmV1dHJhbCBzZW50aW1lbnQgY2F0ZWdvcmllcwpnZ3Bsb3QobXlfZGF0YV9oYmQsIGFlcyh4ID0gc2VudGltZW50X2NhdGVnb3J5LCBmaWxsID0gc2VudGltZW50X2NhdGVnb3J5KSkgKwogIGdlb21fYmFyKCkgKwogIGdlb21fdGV4dChzdGF0ID0gImNvdW50IiwgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsKICBsYWJzKHRpdGxlID0gIlNlbnRpbWVudCBDYXRlZ29yaWVzIFByb3BvcnRpb25zIEJhc2VkIG9uIFN5dXpoZXQgUGFja2FnZSIsCiAgICAgICB4ID0gIlNlbnRpbWVudCBDYXRlZ29yeSIsIHkgPSAiTnVtYmVyIG9mIEFydGljbGVzIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXk5MCIsIGNvbG9yID0gTkEpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQoKYGBgCgoKYGBge3J9CiMgVHJ5IHRvIGV4cGxvcmUgc2VudGltZW50IGNoYW5nZSBvdmVyIHRpbWUgYmFzZWQgb24gc3l1emhldCBwYWNrYWdlCiMgQ29udmVydCBmaXJzdF9wdWJsaWNhdGlvbl9kYXRlIHRvIGEgZGF0ZSBvYmplY3QKbXlfZGF0YV9oYmQkZmlyc3RfcHVibGljYXRpb25fZGF0ZSA8LSBhcy5EYXRlKG15X2RhdGFfaGJkJGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUpCgojIEFnZ3JlZ2F0ZSBzZW50aW1lbnQgc2NvcmUgYnkgZGF0ZQpzZW50aW1lbnRfYnlfZGF0ZSA8LSBteV9kYXRhX2hiZCAlPiUKICBncm91cF9ieShmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKSAlPiUKICBzdW1tYXJpc2Uoc2VudGltZW50X3Njb3JlID0gbWVhbihzZW50aW1lbnRfc2NvcmUpKQoKIyBDcmVhdGUgYSBsaW5lIGNoYXJ0IG9mIHNlbnRpbWVudCBzY29yZSBvdmVyIHRpbWUKZ2dwbG90KHNlbnRpbWVudF9ieV9kYXRlLCBhZXMoeD1maXJzdF9wdWJsaWNhdGlvbl9kYXRlLCB5PXNlbnRpbWVudF9zY29yZSkpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiIzAwMDA4MCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvciA9ICJyZWQiKSArCiAgbGFicyh0aXRsZT0iU2VudGltZW50IFNjb3JlIG92ZXIgVGltZSBCYXNlZCBvbiBTeXV6aGV0IFBhY2thZ2UiLCB4PSJEYXRlIiwgeT0iU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCgpgYGB7cn0KIyBQbG90IHRoZSBzZW50aW1lbnQgc2NvcmUgb3ZlciB0aW1lIHRvZ2V0aGVyIChCaW5nIExleGljb24gYW5kIFN5dXpoZXQgUGFja2FnZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoc2NhbGVzKQoKIyBDcmVhdGUgdGhlIGZpcnN0IHBsb3QKcGxvdGEgPC0gZ2dwbG90KHNlbnRpbWVudF9jYXRlZ29yeV9kYXRlLCBhZXMoeCA9IGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIHkgPSBzZW50aW1lbnRfc2NvcmUpKSArCiAgZ2VvbV9saW5lKGNvbG9yID0gIiMwMDAwODAiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAicmVkIikgKwogIGxhYnModGl0bGUgPSAiU2VudGltZW50IFNjb3JlIE92ZXIgVGltZSBCYXNlZCBvbiBCaW5nIExleGljb24iLAogICAgICAgeCA9ICJEYXRlIiwKICAgICAgIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjIgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJXkiKQoKIyBDcmVhdGUgdGhlIHNlY29uZCBwbG90CnBsb3RiIDwtIGdncGxvdChzZW50aW1lbnRfYnlfZGF0ZSwgYWVzKHg9Zmlyc3RfcHVibGljYXRpb25fZGF0ZSwgeT1zZW50aW1lbnRfc2NvcmUpKSArCiAgZ2VvbV9saW5lKGNvbG9yID0gIiMwMDAwODAiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAicmVkIikgKwogIGxhYnModGl0bGU9IlNlbnRpbWVudCBTY29yZSBvdmVyIFRpbWUgQmFzZWQgb24gU3l1emhldCBQYWNrYWdlIiwgeD0iRGF0ZSIsIHk9IlNlbnRpbWVudCBTY29yZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiViICV5IikKCiMgQ29tYmluZSB0aGUgcGxvdHMgdmVydGljYWxseSB1c2luZyBwYXRjaHdvcmsKcGxvdGEgLyBwbG90YgpgYGAKCgpgYGB7cn0KIyMgU3VwZXJ2aXNlZCBNYWNoaW5lIExlYXJuaW5nCiMgVHJ5IHRvIGFwcGx5IG1hY2hpbmUgbGVhcm5pbmcgdGVjaG5pcXVlLCBidWlsZCBhIG1vZGVsIHRvIHByZWRpY3Qgc2VudGltZW50CiMgTWFwIHRoZSB2YWx1ZXMgb2YgdGhlIG9yaWdpbmFsIGNvbHVtbiAic2VudGltZW50X2NhdGVnb3J5IiB0byBpbnRlZ2VycwpteV9kYXRhX21sIDwtIG15X2RhdGFfaGJkICU+JQogIG11dGF0ZShpZCA9IHJvd19udW1iZXIoKSwgIyBhZGQgYSBuZXcgY29sdW1uIHdpdGggcm93IG51bWJlcnMKICAgICAgICAgc2VudGltZW50X2NhdGVnb3J5ID0gY2FzZV93aGVuKAogICAgICAgICAgIHNlbnRpbWVudF9jYXRlZ29yeSA9PSAiUG9zaXRpdmUiIH4gMiwKICAgICAgICAgICBzZW50aW1lbnRfY2F0ZWdvcnkgPT0gIk5lZ2F0aXZlIiB+IDEsCiAgICAgICAgICAgc2VudGltZW50X2NhdGVnb3J5ID09ICJOZXV0cmFsIiB+IDAsCiAgICAgICAgICAgVFJVRSB+IE5BX3JlYWxfICMgdG8gaGFuZGxlIGFueSBvdGhlciBjYXNlcwogICAgICAgICApKSAKYGBgCgpgYGB7cn0KIyBFeHRyYWN0IHVuaWdyYW1zLCBiaWdyYW1zIGFzIGZlYXR1cmVzCnVuaWdyYW1zIDwtIG15X2RhdGFfbWwkYm9keV90ZXh0ICU+JQogICAgICAgICAgdG9rZW5zKHJlbW92ZV9wdW5jdCA9IFQpICU+JQogICAgICAgICAgdG9rZW5zX25ncmFtcygxKSAlPiUKICAgICAgICAgIGRmbSgpCmJpZ3JhbXMgPC0gIG15X2RhdGFfbWwkYm9keV90ZXh0ICU+JQogICAgICAgICAgdG9rZW5zKHJlbW92ZV9wdW5jdCA9IFQpICU+JQogICAgICAgICAgdG9rZW5zX25ncmFtcygyKSAlPiUKICAgICAgICAgIGRmbSgpCiMgQXBwbHkgc3BhcnNpdHkgY29ycmVjdGlvbiB0byByZWR1Y2UgdGhlIHplcm8gY291bnRzCnVuaWdyYW1zX2NvcnJlY3RlZCA8LSBkZm1fdHJpbSh1bmlncmFtcywgc3BhcnNpdHkgPSAuOTApCmJpZ3JhbXNfY29ycmVjdGVkIDwtIGRmbV90cmltKGJpZ3JhbXMsIHNwYXJzaXR5ID0gLjkwKQoKIyBDaGVjayBkaW1lbnNpb25zCmRpbSh1bmlncmFtc19jb3JyZWN0ZWQpCgojIFRGSURGLXdlaWdodGVkIG5ncmFtcwp1bmlncmFtc190ZmlkZiA8LSBkZm1fdGZpZGYodW5pZ3JhbXNfY29ycmVjdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2hlbWVfdGYgPSAnY291bnQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY2hlbWVfZGYgPSAnaW52ZXJzZScpCmJpZ3JhbXNfdGZpZGYgPC0gZGZtX3RmaWRmKGJpZ3JhbXNfY29ycmVjdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNjaGVtZV90ZiA9ICdjb3VudCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2NoZW1lX2RmID0gJ2ludmVyc2UnKQoKIyBDb252ZXJ0IG5ncmFtcyB0byBkYXRhIGZyYW1lcwpuZXdzX3VuaWdyYW1zIDwtIGNvbnZlcnQodW5pZ3JhbXNfdGZpZGYsICdkYXRhLmZyYW1lJykKbmV3c19iaWdyYW1zIDwtIGNvbnZlcnQoYmlncmFtc190ZmlkZiwgJ2RhdGEuZnJhbWUnKQoKIyBBZGQgaWQgY29sdW1uIHRvIG5ncmFtcyB0byBqb2luIHVuaWdyYW1zIGFuZCBiaWdyYW1zIGxhdGVyCm5ld3NfdW5pZ3JhbXMkaWQgPC0gbXlfZGF0YV9tbCRpZApuZXdzX2JpZ3JhbXMkaWQgPC0gbXlfZGF0YV9tbCRpZAoKIyBSZW1vdmUgZG9jX2lkIGNvbHVtbgp1bmlncmFtc190ZmlkZl9kZiA8LSBzdWJzZXQobmV3c191bmlncmFtcywgc2VsZWN0ID0gLWMoZG9jX2lkKSkKYmlncmFtc190ZmlkZl9kZiA8LSBzdWJzZXQobmV3c19iaWdyYW1zLCBzZWxlY3QgPSAtYyhkb2NfaWQpKQoKIyBKb2luIHRoZSB0d28gZGF0YXNldHMKbWxfZGF0YSA8LSBiaWdyYW1zX3RmaWRmX2RmICU+JSBsZWZ0X2pvaW4odW5pZ3JhbXNfdGZpZGZfZGYsIGJ5ID0gImlkIikKYGBgCgpgYGB7cn0KIyBHZXQgc2VudGltZW50IHNjb3JlcyBhcyBmZWF0dXJlcwptbF9kYXRhJHNlbnRpbWVudF9zY29yZSA8LSBnZXRfc2VudGltZW50KG15X2RhdGFfbWwkYm9keV90ZXh0KQojIEFkZCBsYWJlbCBjb2x1bW4KbWxfZGF0YSRsYWJlbCA8LSBteV9kYXRhX21sJHNlbnRpbWVudF9jYXRlZ29yeQojIFJlbW92ZSB0aGUgaWQgY29sdW1uCm1sX2RhdGEgPC0gbWxfZGF0YSAlPiUgc2VsZWN0KC1pZCkKIyBNYXAgbGFiZWxzCm1sX2RhdGEkbGFiZWwgPC0gaWZlbHNlKG1sX2RhdGEkbGFiZWwgPT0gIjIiLCAiUG9zaXRpdmUiLCAKICAgICAgICAgICAgICAgICAgIGlmZWxzZShtbF9kYXRhJGxhYmVsID09ICIxIiwgIk5lZ2F0aXZlIiwgIk5ldXRyYWwiKSkgCiMgQ29udmVydCBsYWJlbCB0byBmYWN0b3IKbWxfZGF0YSRsYWJlbCA8LSBhcy5mYWN0b3IobWxfZGF0YSRsYWJlbCkgIyBjb252ZXJ0IGxhYmVsIHRvIGZhY3RvcgpgYGAKCmBgYHtyfQojIEFwcGx5IG1hY2hpbmUgbGVhcm5pbmcgc2V0dGluZwpsaWJyYXJ5KGNhcmV0KQpzZXQuc2VlZCgxMjMpCmZvcl90cmFpbmluZyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHkgPSBtbF9kYXRhJGxhYmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCA9IC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFKQoKdHJhaW5pbmdfZGF0YSA8LSBtbF9kYXRhW2Zvcl90cmFpbmluZyxdCnRlc3RpbmdfZGF0YSA8LSBtbF9kYXRhWy1mb3JfdHJhaW5pbmcsXQpgYGAKCgpgYGB7cn0KIyBTZXQgdHJhaW5pbmcgY29udHJvbCBwYXJhbWV0ZXJzCnRyYWluaW5nX2NvbnRyb2xzIDwtIHRyYWluQ29udHJvbChtZXRob2Q9ImN2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzUHJvYnMgPSBUKQpgYGAKCmBgYHtyfQojIyBUcmFpbiB0aGUgU1ZNIG1vZGVsCm1vZGVsLnN2bSA8LSB0cmFpbihsYWJlbCB+IC4sCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbmluZ19kYXRhLAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtTGluZWFyIiwKICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5pbmdfY29udHJvbHMsCiAgICAgICAgICAgICAgICAgIHByZVByb2Nlc3MgPSBjKCd6dicpCiAgICAgICAgICAgICAgICAgKQpgYGAKCgpgYGB7cn0KIyBQcmVkaWN0IG9uIHRoZSB0ZXN0aW5nIGRhdGEKcHJlZF9zdm0gPC0gcHJlZGljdChtb2RlbC5zdm0sIHRlc3RpbmdfZGF0YSkKCiMgRXZhbHVhdGUgdGhlIG1vZGVsCmNvbmZ1c2lvbk1hdHJpeChwcmVkX3N2bSwgYXMuZmFjdG9yKHRlc3RpbmdfZGF0YSRsYWJlbCksIG1vZGU9InByZWNfcmVjYWxsIikKYGBgCgoKYGBge3J9CiMjIFRyYWluIHRoZSBOYWl2ZSBCYXllcyBtb2RlbAptb2RlbC5uYiA8LSB0cmFpbihsYWJlbCB+IC4sCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbmluZ19kYXRhLAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAibmFpdmVfYmF5ZXMiLAogICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbmluZ19jb250cm9scywKICAgICAgICAgICAgICAgICAgcHJlUHJvY2VzcyA9IGMoJ3p2JykpCmBgYAoKCmBgYHtyfQojIFByZWRpY3Qgb24gdGhlIHRlc3RpbmcgZGF0YQpwcmVkX25iIDwtIHByZWRpY3QobW9kZWwubmIsIHRlc3RpbmdfZGF0YSkKCiMgRXZhbHVhdGUgdGhlIG1vZGVsCmNvbmZ1c2lvbk1hdHJpeChwcmVkX25iLCBhcy5mYWN0b3IodGVzdGluZ19kYXRhJGxhYmVsKSwgbW9kZT0icHJlY19yZWNhbGwiKQpgYGAKCmBgYHtyfQojIFNldCB0cmFpbmluZyBjb250cm9sIHBhcmFtZXRlcnMKdHJhaW5pbmdfY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciA9IDUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlSXRlciA9IEZBTFNFKQpgYGAKCmBgYHtyfQojIFRyYWluIHRoZSBOZXVyYWwgTmV0d29yayAoTk4pIG1vZGVsCm1vZGVsLm5uIDwtIHRyYWluKGxhYmVsIH4gLiwKICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluaW5nX2RhdGEsCiAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluaW5nX2NvbnRyb2wsCiAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJubmV0IiwKICAgICAgICAgICAgICAgICAgTWF4Tld0cyA9IDEwMDAwLAogICAgICAgICAgICAgICAgICBtYXhpdCA9IDEwKQpgYGAKCmBgYHtyfQojIFByZWRpY3Qgb24gdGhlIHRlc3RpbmcgZGF0YQpwcmVkX25uIDwtIHByZWRpY3QobW9kZWwubm4sIHRlc3RpbmdfZGF0YSkKIyBFdmFsdWF0ZSB0aGUgbW9kZWwKY29uZnVzaW9uTWF0cml4KHByZWRfbm4sIHRlc3RpbmdfZGF0YSRsYWJlbCwgbW9kZSA9ICJwcmVjX3JlY2FsbCIpCmBgYAoKCmBgYHtyfQojIyBVbnN1cGVydmlzZWQgTWFjaGluZSBMZWFybmluZwojIFRyeSB0byBhcHBseSBrLW1lYW5zIGNsdXN0ZXJpbmcgbW9kZWwgdXNpbmcgdGhlIGV4dHJhY3RlZCBmZWF0dXJlcwojIFVzZSBuZ3JhbXMgZnJlcXVlbmNpZXMgYXMgZmVhdHVyZXMKbWxfZ3JhbXMgPC0gYmlncmFtc190ZmlkZl9kZiAlPiUgbGVmdF9qb2luKHVuaWdyYW1zX3RmaWRmX2RmLCBieSA9ICJpZCIpCiMgUmVtb3ZlIHRoZSBpZCBjb2x1bW4KbWxfZ3JhbXMgPC0gbWxfZ3JhbXMgJT4lIHNlbGVjdCgtaWQpCmBgYAoKYGBge3J9CiMgVXNlIHRoZSBrLW1lYW5zIG1vZGVsIG9uIG4tZ3JhbXMKIyBEZXRlcm1pbmUgawp3c3MgPC0gbnVtZXJpYygpCmZvcihpIGluIDE6MTApewogIG5ncmFtc19rbWVhbnNfdGVtcCA8LSBrbWVhbnMoeCA9IG1sX2dyYW1zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSBpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGl0ZXIubWF4ID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gMTApCiAgd3NzW2ldIDwtIG5ncmFtc19rbWVhbnNfdGVtcCR0b3Qud2l0aGluc3MKfQp7cGxvdCh3c3MsIHR5cGU9J2InKX0gCgpgYGAKCmBgYHtyfQojIFNldHRsZSBmb3Igaz0yCm5ncmFtc19rbWVhbnMgPC0ga21lYW5zKG1sX2dyYW1zLAogICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICBpdGVyLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gMTApCmBgYAoKYGBge3J9CiMgTG9vayBhdCB0aGUgY2x1c3RlcnMgYnkgdGhlaXIgbW9zdCBwcm9taW5lbnQgbmdyYW0gZGlmZmVyZW5jZXMKIyBDbHVzdGVyIDEKY2F0KCJDbHVzdGVyIDE6IFxuIikKcHJpbnQoaGVhZChzb3J0KG5ncmFtc19rbWVhbnMkY2VudGVyc1sxLCBdLCBkZWNyZWFzaW5nID0gVFJVRSksIG4gPSAyMCkpCiMgQ2x1c3RlciAyCmNhdCgiXG5DbHVzdGVyIDI6IFxuIikKcHJpbnQoaGVhZChzb3J0KG5ncmFtc19rbWVhbnMkY2VudGVyc1syLCBdLCBkZWNyZWFzaW5nID0gVFJVRSksIG4gPSAyMCkpCmBgYAoKYGBge3J9CiMgVXNlIHRoZSBrLW1lYW5zIG1vZGVsIG9uIHNlbnRpbWVudCBzY29yZQojIERldGVybWluZSBrCndzcyA8LSBudW1lcmljKCkKZm9yKGkgaW4gMToxMCl7CiAgc2VudGltZW50X2ttZWFuc190ZW1wIDwtIGttZWFucyh4ID0gbWxfZGF0YSRzZW50aW1lbnRfc2NvcmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVycyA9IGksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXRlci5tYXggPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc3RhcnQgPSAxMCkKICB3c3NbaV0gPC0gc2VudGltZW50X2ttZWFuc190ZW1wJHRvdC53aXRoaW5zcwp9CntwbG90KHdzcywgdHlwZT0nYicpfQpgYGAKCgpgYGB7cn0KIyBTZXR0bGUgZm9yIGs9MwpzZW50aW1lbnRfa21lYW5zIDwtIGttZWFucyhtbF9kYXRhJHNlbnRpbWVudF9zY29yZSwKICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSAzLAogICAgICAgICAgICAgICAgICAgICAgaXRlci5tYXggPSA1LAogICAgICAgICAgICAgICAgICAgICAgbnN0YXJ0ID0gNSkKYGBgCgoKYGBge3J9CiMgUHJpbnQgdGhlIGNlbnRlcnMgb2YgZWFjaCBzZW50aW1lbnQgY2x1c3RlcgpwcmludChzZW50aW1lbnRfa21lYW5zJGNlbnRlcnNbMSwgXSkKcHJpbnQoc2VudGltZW50X2ttZWFucyRjZW50ZXJzWzIsIF0pCnByaW50KHNlbnRpbWVudF9rbWVhbnMkY2VudGVyc1szLCBdKQpgYGAKCmBgYHtyfQojIFZpc3VhbGlzZSB0aGUgY2x1c3RlcnMKIyBBZGQgdGhlIGNsdXN0ZXIgbGFiZWxzIHRvIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lCm1sX2RhdGEkY2x1c3RlciA8LSBmYWN0b3Ioc2VudGltZW50X2ttZWFucyRjbHVzdGVyKQoKIyBDcmVhdGUgdGhlIGJveHBsb3QKZ2dwbG90KG1sX2RhdGEsIGFlcyh4ID0gY2x1c3RlciwgeSA9IHNlbnRpbWVudF9zY29yZSkpICsKICBnZW9tX2JveHBsb3QoKSArCiAgeGxhYigiQ2x1c3RlciIpICsKICB5bGFiKCJTZW50aW1lbnQgU2NvcmUiKQoKYGBgCgoKYGBge3J9CiMjIyBUb3BpYyBNb2RlbGxpbmcKIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocXVhbnRlZGEpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkodG9waWNtb2RlbHMpCmxpYnJhcnkobGRhdHVuaW5nKQpsaWJyYXJ5KHN0bSkKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkodG0pCmxpYnJhcnkoZ2dwbG90MikKYGBgCgpgYGB7cn0KIyBQcmVwYXJlIHRoZSBkYXRhIGFuZCBzZWxlY3QgcmVxdWlyZWQgY29sdW1ucwpteV9kYXRhX2hiZCA8LSBteV9kYXRhICU+JSAKICBzZWxlY3QoaGVhZGxpbmUsIGJvZHlfdGV4dCwgZmlyc3RfcHVibGljYXRpb25fZGF0ZSkKIyBSZW1vdmUgZHVwbGljYXRlcwpteV9kYXRhX2hiZCA8LSBkaXN0aW5jdChteV9kYXRhX2hiZCkKIyBSZW1vdmUgYW55IG5vaXNlIGluIHRoZSBkYXRhCm15X2RhdGFfaGJkJGJvZHlfdGV4dCA8LSBnc3ViKCJcXFcrIiwgIiAiLCBteV9kYXRhX2hiZCRib2R5X3RleHQpCmBgYAoKCmBgYHtyfQojIFRva2VuaXNlIHRoZSBkYXRhIGFuZCByZW1vdmUgcHVuY3R1YXRpb24sIG51bWJlcnMsIGFuZCBzeW1ib2xzCiMgQ29udmVydCBhbGwgdGV4dCB0byBsb3dlcmNhc2UsIGFwcGx5IHN0ZW1taW5nLCBhbmQgcmVtb3ZlIG5vbi1pbmZvcm1hdGl2ZSB3b3JkcyBmb3IgdG9waWMgbW9kZWxsaW5nIHB1cnBvc2VzCnRtX3Rva2VucyA8LSB0b2tlbnMobXlfZGF0YV9oYmQkYm9keV90ZXh0LCByZW1vdmVfcHVuY3QgPSBUUlVFLCByZW1vdmVfbnVtYmVycyA9IFRSVUUsIHJlbW92ZV9zeW1ib2xzID0gVFJVRSkgJT4lIHRva2Vuc19yZW1vdmUoc3RvcHdvcmRzKCJlbmdsaXNoIikpICU+JSB0b2tlbnNfdG9sb3dlcigpICU+JSB0b2tlbnNfd29yZHN0ZW0obGFuZ3VhZ2UgPSBxdWFudGVkYV9vcHRpb25zKCJsYW5ndWFnZV9zdGVtbWVyIikpICU+JSB0b2tlbnNfcmVtb3ZlKGMoInBvbGljIiwgIm9mZmljIiwgInBlb3BsIiwgInllYXIiLCAiZGF5IiwgIndpbGwiLCAiYWxzbyIsICJ0IiwgInMiLCAiY2FuIiwgInVrIiwgImJyaXRpc2giKSkKYGBgCgpgYGB7cn0KIyBDcmVhdGUgYSBEb2N1bWVudCBGZWF0dXJlIE1hdHJpeCAoZGZtKQpkZm1fdG0gPC0gZGZtKHRtX3Rva2VucykKCiMgVHJpbSB0aGUgZGZtIGZvciBmcmVxdWVudGx5IGFuZCByYXJlbHkgb2NjdXJyaW5nIHRlcm1zCmRmbS50cmltIDwtIGRmbV90cmltKGRmbV90bSwgbWluX2RvY2ZyZXEgPSAwLjA3NSwgbWF4X2RvY2ZyZXEgPSAwLjksIGRvY2ZyZXFfdHlwZSA9ICJwcm9wIikKZGZtLnRyaW0KYGBgCgpgYGB7cn0KIyMgTERBIHRvcGljIG1vZGVsbGluZwpuLnRvcGljcyA8LSA1CmRmbTJ0b3BpY21vZGVscyA8LSBjb252ZXJ0KGRmbS50cmltLCB0byA9ICJ0b3BpY21vZGVscyIpCmxkYS5tb2RlbCA8LSBMREEoZGZtMnRvcGljbW9kZWxzLCBuLnRvcGljcywgY29udHJvbCA9IGxpc3Qoc2VlZD0xMTEpKQpsZGEubW9kZWwKYGBgCgpgYGB7cn0KIyBFeHRyYWN0IHRoZSB0b3BpYy10ZXJtIG1hdHJpeCAoYmV0YSkgZnJvbSB0aGUgTERBIG1vZGVsCmJldGFfdG9waWNzIDwtIHRpZHkobGRhLm1vZGVsLCBtYXRyaXggPSAiYmV0YSIpCmJldGFfdG9waWNzCmBgYAoKYGBge3J9CiMgR3JvdXAgdGhlIHRlcm1zIGJ5IHRvcGljcwpiZXRhX3RvcF90ZXJtcyA8LSBiZXRhX3RvcGljcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgc2xpY2VfbWF4KGJldGEsIG4gPSAxMCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UodG9waWMsIC1iZXRhKQpgYGAKCmBgYHtyfQojIERpc3BsYXkgdGhlIGdyb3VwIHRlcm1zIApiZXRhX3RvcF90ZXJtcyAlPiUKICBtdXRhdGUodGVybSA9IHJlb3JkZXJfd2l0aGluKHRlcm0sIGJldGEsIHRvcGljKSkgJT4lCiAgZ2dwbG90KGFlcyhiZXRhLCB0ZXJtLCBmaWxsID0gZmFjdG9yKHRvcGljKSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+IHRvcGljLCBzY2FsZXMgPSAiZnJlZSIpICsKICBzY2FsZV95X3Jlb3JkZXJlZCgpCmBgYAoKYGBge3J9CiMgVG9wIDEwIHRlcm1zIGZvciBlYWNoIHRvcGljcyAKYXMuZGF0YS5mcmFtZSh0ZXJtcyhsZGEubW9kZWwsIDEwKSkKCiMgTGlzdCB0aGUgdG9waWMgbnVtYmVyIGZvciBlYWNoIG5ld3MgYXJ0aWNsZSBpbiB0aGUgY29ycHVzLgpkYXRhLmZyYW1lKFRvcGljID0gdG9waWNzKGxkYS5tb2RlbCkpCmBgYAoKYGBge3J9CiMgQ29udmVydCB0aGUgdG9waWMgbW9kZWwgdG8gYSBkYXRhIGZyYW1lCnRvcGljc19kZiA8LSBkYXRhLmZyYW1lKHRvcGljcyhsZGEubW9kZWwpKQoKIyBQbG90IHRoZSBjb3VudCBvZiB0b3BpY3MKZ2dwbG90KHRvcGljc19kZiwgYWVzKHggPSBmYWN0b3IodG9waWNzKGxkYS5tb2RlbCkpKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsID0gZmFjdG9yKHRvcGljcyhsZGEubW9kZWwpKSksIGFscGhhID0gMC44KSArCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlRvcGljIikgKwogIGxhYnModGl0bGUgPSAiVG9waWMgRGlzdHJpYnV0aW9uIEJhc2VkIG9uIExhdGVudCBEaXJpY2hsZXQgQWxsb2NhdGlvbiAoTERBKSIsIHggPSAiVG9waWMiLCB5ID0gIkNvdW50IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyYXk5MCIpKSArCiAgZ2VvbV90ZXh0KHN0YXQ9J2NvdW50JywgYWVzKGxhYmVsPS4uY291bnQuLiksIHZqdXN0PS0wLjUpCgpgYGAKCgoKYGBge3J9CiMjIFN0cnVjdHVyYWwgVG9waWMgTW9kZWxpbmcgKFNUTSkKbi50b3BpY3MgPC0gNQpkZm0yc3RtIDwtIGNvbnZlcnQoZGZtLnRyaW0sIHRvID0gInN0bSIpCm1vZGVsbC5zdG0gPC0gc3RtKGRmbTJzdG0kZG9jdW1lbnRzLCBkZm0yc3RtJHZvY2FiLCBLID0gbi50b3BpY3MsIGRhdGEgPSBkZm0yc3RtJG1ldGEsIGluaXQudHlwZSA9ICJTcGVjdHJhbCIpCmBgYAoKYGBge3J9CiMgVG9wIDEwIHRlcm1zIGZvciBlYWNoIHRvcGljCmFzLmRhdGEuZnJhbWUodChsYWJlbFRvcGljcyhtb2RlbGwuc3RtLCBuID0gMTApJHByb2IpKQoKIyBHZW5lcmF0ZSBhIHdvcmQgY2xvdWQgZm9yIHRoZSBmaXJzdCB0b3BpYwpwYXIobWFyPWMoMC41LCAwLjUsIDAuNSwgMC41KSkKY2xvdWQobW9kZWxsLnN0bSwgdG9waWMgPSAxLCBzY2FsZSA9IGMoMi4yNSwuNSkpCgojIFBsb3QgYSBzdW1tYXJ5IG9mIHRoZSBlc3RpbWF0ZWQgdG9waWMgc2hhcmVzIGZvciB0aGUgY29ycHVzCnBsb3QobW9kZWxsLnN0bSwgdHlwZSA9ICJzdW1tYXJ5IiwgdGV4dC5jZXggPSAwLjUsIG1haW4gPSAiVG9waWMgc2hhcmVzIG9uIHRoZSBjb3JwdXMgYXMgYSB3aG9sZSIsIHhsYWIgPSAiZXN0aW1hdGVkIHNoYXJlIG9mIHRvcGljcyIpCmBgYAoKCmBgYHtyfQojIFByaW50IHRoZSB0b3AgNSB0ZXJtcyBmb3IgZWFjaCBvZiB0aGUgNSB0b3BpY3MKbGFiZWxUb3BpY3MobW9kZWxsLnN0bSx0b3BpY3MgPSBjKDE6NSksIG49MTApCgojIFBsb3QgaGlzdG9ncmFtcyBvZiB0aGUgZXN0aW1hdGVkIHRvcGljIHNoYXJlcyB3aXRoaW4gdGhlIGRvY3VtZW50cyBmb3IgMyByYW5kb21seSBzZWxlY3RlZCB0b3BpY3MKcGxvdChtb2RlbGwuc3RtLCB0eXBlID0gImhpc3QiLCB0b3BpY3MgPSBzYW1wbGUoMTpuLnRvcGljcywgc2l6ZSA9IDMpLCBtYWluID0gImhpc3RvZ3JhbSBvZiB0aGUgdG9waWMgc2hhcmVzIHdpdGhpbiB0aGUgZG9jdW1lbnRzIikKCiMgUGxvdCB0aGUgdG9wIHRlcm1zIGZvciBlYWNoIG9mIHRoZSA1IHRvcGljcwpwbG90KG1vZGVsbC5zdG0sIHR5cGUgPSAibGFiZWxzIiwgdG9waWNzID0gYygxLDIsMyw0LDUpLCBtYWluID0gIlRvcGljIHRlcm1zIikKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQojIElkZW50aWZ5IHRoZSBtb3N0IHByb2JhYmxlIHRvcGljIGZvciBlYWNoIGRvY3VtZW50CnRvcF90b3BpY3MgPC0gYXBwbHkobW9kZWxsLnN0bSR0aGV0YSwgMSwgd2hpY2gubWF4KQojIEFkZCB0aGUgdG9waWMgbGFiZWxzIHRvIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lCm15X2RhdGFfaGJkJFRvcGljX3N0bSA8LSB0b3BfdG9waWNzCiMgQ2FsY3VsYXRlIHRoZSBjb3VudCBvZiBlYWNoIHRvcGljCmRmX3RvcGljX2NvdW50cyA8LSBteV9kYXRhX2hiZCAlPiUKICBncm91cF9ieShUb3BpY19zdG0pICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSkKCiMgQ3JlYXRlIHRoZSBwbG90CmdncGxvdChkZl90b3BpY19jb3VudHMsIGFlcyh4ID0gZmFjdG9yKFRvcGljX3N0bSksIHkgPSBjb3VudCwgZmlsbCA9IGZhY3RvcihUb3BpY19zdG0pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJUb3BpYyIpICsKICBsYWJzKHRpdGxlID0gIlRvcGljIERpc3RyaWJ1dGlvbiBCYXNlZCBvbiBTdHJ1Y3R1cmFsIFRvcGljIE1vZGVsaW5nIChTVE0pIiwgeCA9ICJUb3BpYyIsIHkgPSAiQ291bnQiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiZ3JheTkwIikpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY291bnQpLCB2anVzdD0tMC41KQoKYGBgCgoKCmBgYHtyfQojIyBUbyBzZWUgaG93IHRoZSB0b3BpY3MgY2hhbmdlZCBvdmVyIHRpbWUgKExEQSBtb2RlbCkKCiMgQWRkIHRoZSB0b3BpYyBsYWJlbHMgdG8gdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUKbXlfZGF0YV9oYmQkVG9waWNfbGRhIDwtIHRvcGljcyhsZGEubW9kZWwpCgojIENvbnZlcnQgdGhlIGRhdGUgY29sdW1uIHRvIGEgRGF0ZSBvYmplY3QKbXlfZGF0YV9oYmQkZmlyc3RfcHVibGljYXRpb25fZGF0ZSA8LSBhcy5EYXRlKG15X2RhdGFfaGJkJGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIGZvcm1hdCA9ICIlWS0lbS0lZCIpCgojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHRvcGljIGJ5IHllYXIKdG9waWNfcHJvcG9ydGlvbnMgPC0gbXlfZGF0YV9oYmQgJT4lCiAgZ3JvdXBfYnkoVG9waWNfbGRhLCB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUpKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBtdXRhdGUocHJvcG9ydGlvbiA9IGNvdW50IC8gc3VtKGNvdW50KSkKCiMgUGxvdCB0aGUgdG9waWMgcHJvcG9ydGlvbnMgb3ZlciB0aW1lCmdncGxvdCh0b3BpY19wcm9wb3J0aW9ucywgYWVzKHggPSB5ZWFyLCB5ID0gcHJvcG9ydGlvbiwgY29sb3IgPSBmYWN0b3IoVG9waWNfbGRhKSkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICJUb3BpYyIpICsKICB0aGVtZV9idygpCgojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHRvcGljIGJ5IGhhbGYteWVhcgp0b3BpY19wcm9wb3J0aW9uc19oYWxmX3llYXIgPC0gbXlfZGF0YV9oYmQgJT4lCiAgZ3JvdXBfYnkoVG9waWNfbGRhLCB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUpLCBoYWxmX3llYXIgPSBsdWJyaWRhdGU6OmZsb29yX2RhdGUoZmlyc3RfcHVibGljYXRpb25fZGF0ZSwgdW5pdCA9ICI2IG1vbnRocyIpKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCksIC5ncm91cHMgPSAnZHJvcCcpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh5ZWFyLCBoYWxmX3llYXIpICU+JQogIG11dGF0ZShwcm9wb3J0aW9uID0gY291bnQgLyBzdW0oY291bnQpKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUKZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19sZGEpKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgY29sb3IgPSAiVG9waWMiKSArCiAgdGhlbWVfYncoKQpgYGAKCgoKYGBge3J9CiMjIFRvIHNlZSBob3cgdGhlIHRvcGljcyBjaGFuZ2VkIG92ZXIgdGltZSAoU1RNIG1vZGVsKQoKIyBJZGVudGlmeSB0aGUgbW9zdCBwcm9iYWJsZSB0b3BpYyBmb3IgZWFjaCBkb2N1bWVudAp0b3BfdG9waWNzIDwtIGFwcGx5KG1vZGVsbC5zdG0kdGhldGEsIDEsIHdoaWNoLm1heCkKIyBBZGQgdGhlIHRvcGljIGxhYmVscyB0byB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZQpteV9kYXRhX2hiZCRUb3BpY19zdG0gPC0gdG9wX3RvcGljcwoKIyBDYWxjdWxhdGUgdGhlIHByb3BvcnRpb24gb2YgZWFjaCB0b3BpYyBieSB5ZWFyCnRvcGljX3Byb3BvcnRpb25zX3N0bSA8LSBteV9kYXRhX2hiZCAlPiUKICBncm91cF9ieShUb3BpY19zdG0sIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZmlyc3RfcHVibGljYXRpb25fZGF0ZSkpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIG11dGF0ZShwcm9wb3J0aW9uID0gY291bnQgLyBzdW0oY291bnQpKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUKZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX3N0bSwgYWVzKHggPSB5ZWFyLCB5ID0gcHJvcG9ydGlvbiwgY29sb3IgPSBmYWN0b3IoVG9waWNfc3RtKSkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICJUb3BpYyIpICsKICB0aGVtZV9idygpCgojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHRvcGljIGJ5IGhhbGYteWVhcgp0b3BpY19wcm9wb3J0aW9uc19zdG1faGFsZl95ZWFyIDwtIG15X2RhdGFfaGJkICU+JQogIGdyb3VwX2J5KFRvcGljX3N0bSwgeWVhciA9IGx1YnJpZGF0ZTo6eWVhcihmaXJzdF9wdWJsaWNhdGlvbl9kYXRlKSwgaGFsZl95ZWFyID0gbHVicmlkYXRlOjpmbG9vcl9kYXRlKGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIHVuaXQgPSAiNiBtb250aHMiKSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoeWVhciwgaGFsZl95ZWFyKSAlPiUKICBtdXRhdGUocHJvcG9ydGlvbiA9IGNvdW50IC8gc3VtKGNvdW50KSkKCgojIFBsb3QgdGhlIHRvcGljIHByb3BvcnRpb25zIG92ZXIgdGltZQpnZ3Bsb3QodG9waWNfcHJvcG9ydGlvbnNfc3RtX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19zdG0pKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgY29sb3IgPSAiVG9waWMiKSArCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyfQojIFJlZmluZSB0aGUgZ3JhcGhzLCBhZGQgdGhlIHRvcCA1IHRlcm1zIGZvciBlYWNoIHRvcGljIHRvIHRoZSBsZWdlbmQgKExEQSBtb2RlbCkKCiMgRXh0cmFjdCB0aGUgdG9wIDUgdGVybXMgZm9yIGVhY2ggdG9waWMKdG9wX3Rlcm1zIDwtIHRlcm1zKGxkYS5tb2RlbCwgNSkKCiMgQ3JlYXRlIGxhYmVsIGZvciBjb2xvciBsZWdlbmQKbGVnZW5kX2xhYmVsIDwtIHBhc3RlKCJUb3AgNSBUZXJtc1xuIiwKICAgICAgICAgICAgICAgICAgICAgICIxOiAiLCBwYXN0ZSh0b3BfdGVybXNbLDFdLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iLAogICAgICAgICAgICAgICAgICAgICAgIjI6ICIsIHBhc3RlKHRvcF90ZXJtc1ssMl0sIGNvbGxhcHNlID0gIiwgIiksICJcbiIsCiAgICAgICAgICAgICAgICAgICAgICAiMzogIiwgcGFzdGUodG9wX3Rlcm1zWywzXSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIiwKICAgICAgICAgICAgICAgICAgICAgICI0OiAiLCBwYXN0ZSh0b3BfdGVybXNbLDRdLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iLAogICAgICAgICAgICAgICAgICAgICAgIjU6ICIsIHBhc3RlKHRvcF90ZXJtc1ssNV0sIGNvbGxhcHNlID0gIiwgIiksIHNlcCA9ICIiKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUKZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19sZGEpKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgCiAgICAgICBjb2xvciA9IGxlZ2VuZF9sYWJlbCwKICAgICAgIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIExEQSBUb3BpYyBNb2RlbGxpbmciKSArCiAgdGhlbWVfYncoKQoKYGBgCgoKYGBge3J9CiMgUmVmaW5lIHRoZSBncmFwaHMsIGFkZCB0aGUgdG9wIDUgdGVybXMgZm9yIGVhY2ggdG9waWMgdG8gdGhlIGxlZ2VuZCAoU1RNIG1vZGVsKQoKIyBFeHRyYWN0IHRoZSB0b3AgNSB0ZXJtcyBmb3IgZWFjaCB0b3BpYwp0b3BfdGVybXNfc3RtIDwtIGxhYmVsVG9waWNzKG1vZGVsbC5zdG0sIHRvcGljcyA9IGMoMTo1KSwgbj01KQp0b3BfdGVybXNfc3RtJGZyZXgKCiMgQ3JlYXRlIGxhYmVsIGZvciBjb2xvciBsZWdlbmQKbGVnZW5kX2xhYmVsX3N0bSA8LSBwYXN0ZSgiVG9wIDUgVGVybXNcbiIsCiAgICAgICAgICAgICAgICAgICAgICAiMTogIiwgcGFzdGUodG9wX3Rlcm1zX3N0bSRmcmV4WzEsXSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIiwKICAgICAgICAgICAgICAgICAgICAgICIyOiAiLCBwYXN0ZSh0b3BfdGVybXNfc3RtJGZyZXhbMixdLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iLAogICAgICAgICAgICAgICAgICAgICAgIjM6ICIsIHBhc3RlKHRvcF90ZXJtc19zdG0kZnJleFszLF0sIGNvbGxhcHNlID0gIiwgIiksICJcbiIsCiAgICAgICAgICAgICAgICAgICAgICAiNDogIiwgcGFzdGUodG9wX3Rlcm1zX3N0bSRmcmV4WzQsXSwgY29sbGFwc2UgPSAiLCAiKSwgIlxuIiwKICAgICAgICAgICAgICAgICAgICAgICI1OiAiLCBwYXN0ZSh0b3BfdGVybXNfc3RtJGZyZXhbNSxdLCBjb2xsYXBzZSA9ICIsICIpLCBzZXAgPSAiIikKCiMgUGxvdCB0aGUgdG9waWMgcHJvcG9ydGlvbnMgb3ZlciB0aW1lCmdncGxvdCh0b3BpY19wcm9wb3J0aW9uc19zdG1faGFsZl95ZWFyLCBhZXMoeCA9IGhhbGZfeWVhciwgeSA9IHByb3BvcnRpb24sIGNvbG9yID0gZmFjdG9yKFRvcGljX3N0bSkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnMoeCA9ICJIYWxmLVllYXIiLCB5ID0gIlByb3BvcnRpb24iLCAKICAgICAgIGNvbG9yID0gbGVnZW5kX2xhYmVsX3N0bSwKICAgICAgIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIFN0cnVjdHVyYWwgVG9waWMgTW9kZWxpbmcgKFNUTSkiKSArCiAgdGhlbWVfYncoKQoKYGBgCgpgYGB7cn0KIyBUcnkgdG8gY29tYmluZSBzZW50aW1lbnQgYW5kIHRvcGljIGNoYW5nZXMgb3ZlciB0aW1lCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyBDcmVhdGUgYSBsaW5lIGNoYXJ0IG9mIHNlbnRpbWVudCBzY29yZSBvdmVyIHRpbWUKcDEgPC0gZ2dwbG90KHNlbnRpbWVudF9ieV9kYXRlLCBhZXMoeCA9IGZpcnN0X3B1YmxpY2F0aW9uX2RhdGUsIHkgPSBzZW50aW1lbnRfc2NvcmUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArCiAgbGFicyh0aXRsZSA9ICJTZW50aW1lbnQgU2NvcmUgb3ZlciBUaW1lIEJhc2VkIG9uIFN5dXpoZXQgUGFja2FnZSIsIHggPSAiRGF0ZSIsIHkgPSAiU2VudGltZW50IFNjb3JlIikgKwogIHRoZW1lX21pbmltYWwoKSsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSkpCgojIFBsb3QgdGhlIHRvcGljIHByb3BvcnRpb25zIG92ZXIgdGltZSB1c2luZyBMREEKcDIgPC0gZ2dwbG90KHRvcGljX3Byb3BvcnRpb25zX2hhbGZfeWVhciwgYWVzKHggPSBoYWxmX3llYXIsIHkgPSBwcm9wb3J0aW9uLCBjb2xvciA9IGZhY3RvcihUb3BpY19sZGEpKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiSGFsZi1ZZWFyIiwgeSA9ICJQcm9wb3J0aW9uIiwgCiAgICAgICBjb2xvciA9ICJUb3BpYyIsIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIExEQSBUb3BpYyBNb2RlbGxpbmciKSArCiAgdGhlbWVfYncoKQoKIyBQbG90IHRoZSB0b3BpYyBwcm9wb3J0aW9ucyBvdmVyIHRpbWUgdXNpbmcgU1RNCnAzIDwtIGdncGxvdCh0b3BpY19wcm9wb3J0aW9uc19zdG1faGFsZl95ZWFyLCBhZXMoeCA9IGhhbGZfeWVhciwgeSA9IHByb3BvcnRpb24sIGNvbG9yID0gZmFjdG9yKFRvcGljX3N0bSkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnMoeCA9ICJIYWxmLVllYXIiLCB5ID0gIlByb3BvcnRpb24iLCAKICAgICAgIGNvbG9yID0gIlRvcGljIiwKICAgICAgIHN1YnRpdGxlID0gIlRvcGljIENoYW5nZXMgT3ZlciBUaW1lIFVzaW5nIFN0cnVjdHVyYWwgVG9waWMgTW9kZWxpbmciKSArCiAgdGhlbWVfYncoKQoKIyBDb21iaW5lIHRoZSB0aHJlZSBwbG90cyB2ZXJ0aWNhbGx5CnAxICsgcDIgKyBwMyArIHBsb3RfbGF5b3V0KG5jb2wgPSAxKQoKYGBgCgoKCgoKCgo=